package threads;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.logging.Level;

import model.contents.Content;
import net.Downloader;
import net.HTTPDownloader;
import util.LoggingUtils;

public class DownloadQueue implements Runnable {

	// singleton instance
	private static DownloadQueue	_instance;
	private static final int		FAIL_LIMIT	= 3;

	private Queue<Content>			downloadQueue;
	private Map<String, Integer>	failCountTable;
	private Downloader				downloader;
	//RBB
	protected final String className = this.getClass().getName();

	private DownloadQueue() {
		this.downloadQueue = new LinkedList<Content>();
		this.failCountTable = new HashMap<String, Integer>();
		this.downloader = HTTPDownloader.getInstance();
	}

	public static DownloadQueue getInstance() {
		if (_instance == null) {
			_instance = new DownloadQueue();
		}
		return _instance;
	}

	public static void addTaskToDownloadQueue(Content content) {
		getInstance().addTask(content);
	}
	
	public synchronized void addTask(Content content) {
		if (content.hasUpdate()) {
			String remotePath = content.getRemotePath();
			Integer failCount = this.failCountTable.get(remotePath);
			if (failCount == null || failCount < DownloadQueue.FAIL_LIMIT) {
				synchronized (this.downloadQueue) {
					// System.out.println("adding task to download queue: " + remotePath);
					this.downloadQueue.add(content);
					this.downloadQueue.notifyAll();
				}
			} else {
				// System.out.println("fail-limit is reached for file: " + remotePath);
			}
		}
	}

	@Override
	public void run() {
		String classMethod = className + ".run()";
		LoggingUtils.logStart(classMethod + " which should run indefinitely");
		Content content;
		String remotePath;
		boolean downloaded;
		Integer failCount;
		while (true) {
			try {
				synchronized (this.downloadQueue) {
					if (this.downloadQueue.isEmpty()) {
						// wait until there's something to download
						///		+ " because it is empty. Waiting indefinitely for new task");
						//System.out.println("download queue is empty, waiting indefinitely for new task.");
						this.downloadQueue.wait();
					}
					if (!this.downloadQueue.isEmpty()) {
						content = this.downloadQueue.poll();
					} else {
						content = null;
					}
				}
				if(content!=null) {
					remotePath = content.getRemotePath();
					
					downloaded = this.downloader.download(remotePath, content.getLocalPath());
					if (downloaded) {
						content.setHasUpdate(false);
					} else {
						failCount = this.failCountTable.get(remotePath);
						if (failCount == null) {
							failCount = new Integer(1);
						} else {
							failCount++;
						}
						this.failCountTable.put(remotePath, failCount);
						// add failed task to the download queue again,
						// until fail limit reached
						this.addTask(content);

					}
				}
				
			} catch (InterruptedException e) {
				LoggingUtils.log(Level.SEVERE, "Cannot poll from downloadQueue in " + classMethod
						+ " because Runnable was interrupted", e);
				//e.printStackTrace();
			}
			catch (Exception e) {
				LoggingUtils.log(Level.SEVERE, "Cannot poll from downloadQueue in " + classMethod
							+ " because Runnable was interrupted", e);
					//e.printStackTrace();
				}
		}
	}
}
